#include <iostream>
using namespace std;

template <class T>
class Node
{
public:
    T data;
    Node *next;
    Node(T data) : data(data), next(nullptr) {}
};

template <class T>
class Queue
{
private:
    Node<T> *head;
    Node<T> *tail;
    int size;

public:
    Queue() : head(nullptr), tail(nullptr), size(0) {
		cout << "queue no arg constructor" << endl;
	};
	Node<T> *getHead()
	{
		return head;
	}

    Queue(const Queue<T> &other) 
    {
		cout << "copy constructor" << endl;

		//clear();
		this->size = 0;

        Node<T> *current = other.head;
        while (current != nullptr)
        {
            push(current->data);
            current = current->next;
        }
		cout << "copy constructor---" << endl;
    }

    ~Queue()
    {
		cout << "destructor call+++" << endl;
        clear();
		cout << "destructor call---" << endl;
    }
    void clear()
    {
		cout << "clear +++, size = " << size << endl;
#if 1
        Node<T> *current = head;
        while (current != nullptr)
        {
            Node<T> *temp = current;
			cout << "delete element:" << temp << endl;
            current = current->next;
            delete temp;
        }
        size = 0;
        head = nullptr;
		tail = nullptr;
#endif
    }
    bool isEmpty() const
    {
        return (size == 0);
    }
    void push(T data)
    {
		cout << "call push +++, size = " << size << endl;
        Node<T> *newNode = new Node<T>(data);
		cout << "new node:" << newNode << endl;
        if (isEmpty())
        {
            head = newNode;
            tail = newNode;
        }
        else
        {
			cout << "enter else" << endl;
            tail->next = newNode;
            tail = newNode;
        }
        size++;
		cout << "call push ---" << endl;
    }
    void operator+=(T data)
    {
        push(data);
    }
    Queue<T> operator=(Queue &other)
    {
		cout << "operator = call" << endl;
        clear();
        Node<T> *current = other.head;
        while (current != nullptr)
        {
			cout << "+++ push +++" << endl;
            push(current->data);
            current = current->next;
        }
		cout << "operator = ---" << endl;
        return *this;
    }
    Queue operator+(T data)
    {
        Queue<T> result;
        result = *this;
        result.push(data);
        return result;
    }
    T pop()
    {
        if (isEmpty())
        {
            throw "Queue is empty";
        }
        T data = head->data;
        Node<T> *temp = head;
        head = head->next;
        delete temp;
        size--;
        return data;
    }
    T front() const
    {
        if (isEmpty())
        {
            throw "Queue is empty";
        }
        return head->data;
    }
    T back() const
    {
        if (isEmpty())
        {
            throw "Queue is empty";
        }
        return tail->data;
    }
    int getSize() const
    {
        return size;
    }
    friend ostream &operator<<(ostream &os, const Queue<T> &l)
    {
        Node<T> *current = l.head;
        while (current != nullptr)
        {
            os << current->data << " ";
            current = current->next;
        }
        return os;
    }
};
int main()
{
    Queue<string> q;
    q.push("ll");
#if 0
    q.push("oo");
    q.push("rr");
    q.push("tt");
    q.push("yy");
    cout << q << endl;
    q += "zz";
#endif
    cout << "queue q: " << q << endl;
    Queue<string> p;
	cout << "+++ = +++" << endl;
    p = q ;
	cout << "--- = ---" << endl;

	cout << "q.head:" << q.getHead() << endl;
	cout << "p.head:" << p.getHead() << endl;
	cout << "+++" << endl;
    //cout << "queue p:" << p << endl;
    return 0;
}

